/*
 Copyright (c) 2013- MeherTJ G.
 All rights reserved. Everybody can use these code freely.
 */

#pragma once

#include <sstream>
#include <string>
#include <vector>
#include <deque>
#include <cstdio>

namespace Zen {
	/// Hex
	inline char HexChar(int v16, char a = 'A')
	{
		return (v16 > 15 ? 0 :
				(v16 < 10 ? (char)('0'+v16) :
				 (v16 < 0 ? 0 : (char)(a+(v16-10)))
				 )
				);
	}
	
	inline int HexValue(char hex)
	{
		return (hex < '0' ? -1 :
				(hex <= '9' ? hex - '0' :
				 (hex < 'A' ? -1 :
				  (hex <= 'F' ? hex - 'A' + 10 :
				   (hex < 'a' ? -1 :
					(hex <= 'F' ? hex - 'a' + 10 : -1)
					)
				   )
				  )
				 )
				);
	};
	
	inline std::string HexStringFromNumber(uint64_t value)
	{
		if(value == 0) return "0";
		
		std::string temp;
		
		while(value)
		{
			auto r = (uint8_t)(value & 0xf);
			temp.push_back(HexChar(r));
			value >>= 4;
		}
		std::string res;
		for(auto i = temp.rbegin(); i != temp.rend(); ++i) res.push_back(*i);
		return res;
	}
	
	inline uint64_t HexStringToNumber(std::string const & hex)
	{
		uint64_t value = 0;
		for(auto i : hex)
		{
			auto v = HexValue(i);
			if(v == -1) break;
			value <<= 4;
			value |= v;
		}
		return value;
	}
	
	inline std::string HexEncode(void const * data, size_t size)
	{
		std::string res;
		res.reserve(size << 1);
		
		auto buf = (uint8_t const *)data;
		for (size_t i = 0; i < size; ++i)
		{
			res.push_back(Zen::HexChar(buf[i] >> 4));
			res.push_back(Zen::HexChar(buf[i] & 0xf));
		}
		return res;
	}
	
	/**
	 "abc" => "616263"
	 */
	template<typename _Char>
	inline std::string HexEncode(std::vector<_Char> const & data)
	{
		static_assert(sizeof(_Char) == 1, "only char allowed here");
		return HexToString(data.data(), data.size());
	}
	
	inline std::vector<uint8_t> HexDecode(std::string const & s)
	{
		std::vector<uint8_t> data;
		data.reserve(s.size() >> 1);
		
		for(decltype(s.size()) i = 1; i < s.size(); i += 2)
		{
			int v = (Zen::HexValue(s[i-1]) << 4) | Zen::HexValue(s[i]);
			data.push_back((uint8_t)v);
		}
		return data;
	}
}

